package com.example.demo.generator.scaffold.model;

import com.example.demo.generator.codegen.model.ClassInfo;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * 上下文参数集合，用于渲染脚手架模板。
 */
public class TemplateContext {

    private final String moduleName;
    private final String basePackage;
    private final String moduleSimpleName;
    private final String moduleBasePath;
    private final String modulePackage;
    private final String modulePath;
    private final ClassInfo classInfo;
    private final ScaffoldOptions options;
    private final LocalDateTime generatedAt;
    private final String rawCreateTableSql;
    private final ScaffoldOptions.FrontendFramework frontendFramework;

    private TemplateContext(Builder builder) {
        this.moduleName = builder.moduleName;
        this.moduleBasePath = normalizeModuleBasePath(builder.moduleBasePath, builder.moduleName);
        this.basePackage = buildEffectiveBasePackage(builder.basePackage, this.moduleBasePath);
        this.modulePackage = this.basePackage;
        this.modulePath = this.modulePackage.replace('.', '/');
        this.moduleSimpleName = builder.moduleSimpleName != null
                ? builder.moduleSimpleName
                : deriveModuleSimpleName(this.moduleBasePath);
        this.classInfo = builder.classInfo;
        this.options = builder.options != null ? builder.options : ScaffoldOptions.defaultOptions();
        this.generatedAt = builder.generatedAt;
        this.rawCreateTableSql = builder.rawCreateTableSql;
        this.frontendFramework = builder.frontendFramework != null
                ? builder.frontendFramework
                : this.options.getFrontendFramework();
    }

    public String getModuleSimpleName() {
        return moduleSimpleName;
    }

    public String getModuleName() {
        return moduleName;
    }

    public String getBasePackage() {
        return basePackage;
    }

    public String getModuleBasePath() {
        return moduleBasePath;
    }

    public String getModulePackage() {
        return modulePackage;
    }

    public String getModulePath() {
        return modulePath;
    }

    public ClassInfo getClassInfo() {
        return classInfo;
    }

    public ScaffoldOptions getOptions() {
        return options;
    }

    public LocalDateTime getGeneratedAt() {
        return generatedAt;
    }

    public String getRawCreateTableSql() {
        return rawCreateTableSql;
    }

    public ScaffoldOptions.FrontendFramework getFrontendFramework() {
        return frontendFramework;
    }

    public String resolvePackage(String layer) {
        String root = modulePackage;
        if (layer == null || layer.isBlank()) {
            return root;
        }
        return root + "." + layer;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String moduleName;
        private String basePackage;
        private String moduleSimpleName;
        private String moduleBasePath;
        private ClassInfo classInfo;
        private ScaffoldOptions options = ScaffoldOptions.defaultOptions();
        private LocalDateTime generatedAt = LocalDateTime.now();
        private String rawCreateTableSql;
        private ScaffoldOptions.FrontendFramework frontendFramework;

        public Builder moduleName(String moduleName) {
            this.moduleName = moduleName;
            return this;
        }

        public Builder basePackage(String basePackage) {
            this.basePackage = basePackage;
            return this;
        }

        public Builder moduleSimpleName(String moduleSimpleName) {
            this.moduleSimpleName = moduleSimpleName;
            return this;
        }

        public Builder moduleBasePath(String moduleBasePath) {
            this.moduleBasePath = moduleBasePath;
            return this;
        }

        public Builder classInfo(ClassInfo classInfo) {
            this.classInfo = classInfo;
            return this;
        }

        public Builder options(ScaffoldOptions options) {
            this.options = options != null ? options : ScaffoldOptions.defaultOptions();
            return this;
        }

        public Builder generatedAt(LocalDateTime generatedAt) {
            this.generatedAt = generatedAt != null ? generatedAt : LocalDateTime.now();
            return this;
        }

        public Builder rawCreateTableSql(String rawCreateTableSql) {
            this.rawCreateTableSql = rawCreateTableSql;
            return this;
        }

        public Builder frontendFramework(ScaffoldOptions.FrontendFramework frontendFramework) {
            this.frontendFramework = frontendFramework;
            return this;
        }

        public TemplateContext build() {
            Objects.requireNonNull(moduleName, "moduleName must not be null");
            Objects.requireNonNull(basePackage, "basePackage must not be null");
            Objects.requireNonNull(classInfo, "classInfo must not be null");
            return new TemplateContext(this);
        }
    }

    private static String deriveModuleSimpleName(String moduleName) {
        int lastDot = moduleName.lastIndexOf('.');
        if (lastDot == -1) {
            return moduleName;
        }
        return moduleName.substring(lastDot + 1);
    }

    private static String normalizeModuleBasePath(String moduleBasePath, String fallbackModuleName) {
        String value = moduleBasePath;
        if (value == null || value.isBlank()) {
            value = fallbackModuleName;
        }
        value = value.replace('/', '.').replace('\\', '.');
        value = value.replaceAll("\\s+", "");
        value = value.replaceAll("\\.+", ".");
        return trimDots(value);
    }

    private static String buildEffectiveBasePackage(String basePackage, String moduleBasePath) {
        if (basePackage == null || basePackage.isBlank()) {
            return trimDots(moduleBasePath);
        }
        if (moduleBasePath == null || moduleBasePath.isBlank()) {
            return basePackage;
        }
        String normalized = moduleBasePath.replace('/', '.').replace('\\', '.');
        normalized = normalized.replaceAll("\\s+", "");
        normalized = normalized.replaceAll("\\.+", ".");
        normalized = trimDots(normalized);
        if (normalized.isBlank()) {
            return basePackage;
        }

        String rootSegment = basePackage;
        if (basePackage.contains(".")) {
            rootSegment = basePackage.substring(0, basePackage.indexOf('.'));
        }
        rootSegment = trimDots(rootSegment);
        if (rootSegment.isBlank()) {
            rootSegment = "com";
        }
        return rootSegment + "." + normalized;
    }

    private static String trimDots(String input) {
        String result = input;
        while (result.startsWith(".")) {
            result = result.substring(1);
        }
        while (result.endsWith(".")) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }
}
